package com.topc.code.utils;

import cn.hutool.core.util.StrUtil;
import com.topc.base.exception.Asserts;
import com.topc.code.judge.template.CommonCodeSandboxTemplate;
import com.topc.problem.model.code.ExecuteMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.StopWatch;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 进程工具类
 *
 * @author zzx
 */
public class ProcessUtils
{

    /**
     * 执行进程并获取信息
     *
     * @param runProcess
     * @param opName
     * @return
     */
    public static ExecuteMessage compileProcessAndGetMessage(Process runProcess, String opName)
    {
        ExecuteMessage executeMessage = new ExecuteMessage();
        // 记录初始内存使用情况
        long initialMemory = getUsedMemory();
        try
        {
            // 设置计时器
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            // 等待程序执行，获取错误码
            int exitValue = runProcess.waitFor();
            executeMessage.setExitValue(exitValue);
            // 正常退出
            if (exitValue == 0)
            {
                System.out.println(opName + "成功");
                // 分批获取进程的正常输出
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(runProcess.getInputStream()));
                List<String> outputStrList = new ArrayList<>();
                // 逐行读取
                String compileOutputLine;
                while ((compileOutputLine = bufferedReader.readLine()) != null)
                {
                    outputStrList.add(compileOutputLine);
                }
                executeMessage.setMessage(StringUtils.join(outputStrList, "\n"));
            } else
            {
                // 异常退出
                System.out.println(opName + "失败，错误码： " + exitValue);
                // 分批获取进程的正常输出
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(runProcess.getInputStream()));
                List<String> outputStrList = new ArrayList<>();
                // 逐行读取
                String compileOutputLine;
                while ((compileOutputLine = bufferedReader.readLine()) != null)
                {
                    outputStrList.add(compileOutputLine);
                }
                executeMessage.setMessage(StringUtils.join(outputStrList, "\n"));

                // 分批获取进程的错误输出
                BufferedReader errorBufferedReader = new BufferedReader(new InputStreamReader(runProcess.getErrorStream()));
                // 逐行读取
                List<String> errorOutputStrList = new ArrayList<>();
                // 逐行读取
                String errorCompileOutputLine;
                while ((errorCompileOutputLine = errorBufferedReader.readLine()) != null)
                {
                    errorOutputStrList.add(errorCompileOutputLine);
                }
                executeMessage.setErrorMessage(StringUtils.join(errorOutputStrList, "\n"));
            }
            // 记录执行后的内存使用情况
            long finalMemory = getUsedMemory();
            // 计算内存使用量，单位字节，转换成kb需要除以1024
            long memoryUsage = finalMemory - initialMemory;
            executeMessage.setMemory(memoryUsage / 1024);
            stopWatch.stop();
            executeMessage.setTime(stopWatch.getLastTaskTimeMillis());
        } catch (Exception e)
        {
            Asserts.fail("服务器繁忙");
        }
        return executeMessage;
    }

    public static ExecuteMessage runInteractProcessAndGetMessage(Process runProcess,String opName,String args){
        ExecuteMessage executeMessage = new ExecuteMessage();
        long initialMemory = getUsedMemory();
        System.out.println("开始时内存"+initialMemory);
        try{
            //计时器
            //开始计时
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            //获取输出流-----------------------------------------------------------
            OutputStream outputStream = runProcess.getOutputStream();
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
            String[] inputExample = args.split(" ");
            String join = StrUtil.join("\n", inputExample) + "\n";
            //发送数据
            outputStreamWriter.write(join);
            //刷新缓冲区,相当于按下回车
            outputStreamWriter.flush();
            // 等待程序执行，获取错误码
//            int exitValue = runProcess.waitFor();
            int TIME_OUT = 2000; // 设置超时时间为2000毫秒
            boolean finished = runProcess.waitFor(TIME_OUT, TimeUnit.MILLISECONDS);
            int exitValue;
            if (!finished) {
                System.out.println("Finish:超过程序最大运行时间，终止进程");
                runProcess.destroy();
                exitValue = -1;
            } else {
                exitValue = runProcess.exitValue();
            }
            executeMessage.setExitValue(exitValue);
            executeMessage.setExitValue(exitValue);
            //分批获取进程的正常输出------------------------------------------------------------
            InputStream inputStream = runProcess.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            //字符串拼接
            StringBuilder compileOutputStringBuilder = new StringBuilder();
            //逐行读取
            String compileOutputLine;
            while((compileOutputLine=bufferedReader.readLine())!=null){
                compileOutputStringBuilder.append(compileOutputLine+"\n");
            }
            executeMessage.setMessage(compileOutputStringBuilder.toString());
            //关闭流-----------------------------------------------------------------------
            outputStreamWriter.close();
            outputStream.close();
            inputStream.close();
            runProcess.destroy();
            //获取内存
            long usedMemory = getUsedMemory();
            System.out.println("结束内存为"+usedMemory+"b");
            usedMemory-=initialMemory;
            System.out.println("差值内存为"+usedMemory+"b");
            usedMemory= (long) (usedMemory/1024f);
            System.out.println("换算内存为："+usedMemory+"kb");
            executeMessage.setMemory(usedMemory);
            //停止计时
            stopWatch.stop();
            executeMessage.setTime(stopWatch.getLastTaskTimeMillis());

        }catch (Exception e){
            Asserts.fail("服务器繁忙");
        }
        return executeMessage;
    }

    /**
     * 执行交互式进程并获取信息
     *
     * @param runProcess
     * @param args
     * @return
     */
//    public static ExecuteMessage runInteractProcessAndGetMessage(Process runProcess, String args)
//    {
//        ExecuteMessage executeMessage = new ExecuteMessage();
//        try
//        {
//            // 向控制台输入程序
//            OutputStream outputStream = runProcess.getOutputStream();
//            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
//            String[] s = args.split(" ");
//            String join = StrUtil.join("\n", s) + "\n";
//            outputStreamWriter.write(join);
//            // 相当于按了回车，执行输入的发送
//            outputStreamWriter.flush();
//
//            // 分批获取进程的正常输出
//            InputStream inputStream = runProcess.getInputStream();
//            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
//            StringBuilder compileOutputStringBuilder = new StringBuilder();
//            // 逐行读取
//            String compileOutputLine;
//            while ((compileOutputLine = bufferedReader.readLine()) != null)
//            {
//                compileOutputStringBuilder.append(compileOutputLine);
//            }
//            executeMessage.setMessage(compileOutputStringBuilder.toString());
//            // 记得资源的释放，否则会卡死
//            outputStreamWriter.close();
//            outputStream.close();
//            inputStream.close();
//            runProcess.destroy();
//        } catch (Exception e)
//        {
//            Asserts.fail("服务器繁忙");
//        }
//        return executeMessage;
//    }

    /**
     * 获取当前已使用的内存量
     * 单位是byte
     *
     * @return
     */
    public static long getUsedMemory()
    {
        Runtime runtime = Runtime.getRuntime();
        return runtime.totalMemory() - runtime.freeMemory();
    }
}
